/*	Copyright
 
 This file falls within the license domain specified in "IACoreFoundation.h"
 */


#ifndef _IAMath
#define _IAMath






#include <cmath>




class	IAMath
{
public:
	
	
	template <typename IntType>
	static IntType			factorial	( IntType inValue ) {
		IntType outResult = 1 ;
		while ( inValue > 1 )
			outResult *= (inValue--) ;
		return outResult ;
	}
	
	
	
	// Note: uses range (lowerBound,inValue]
	template <typename IntType>
	static IntType			factorial	( IntType inValue , IntType inLowerBound ) {
		IntType outResult = 1 ;
		while ( inValue > inLowerBound )
			outResult *= (inValue--) ;
		return outResult ;
	}
	
	
	
	template <typename IntType>
	static IntType			nCr			( IntType inN , IntType inR ) {
		if ( inR > inN ) return 0 ;
		
		IntType		b1		=	inR ;
		IntType		b2		=	inN - inR ;
		
		IntType		bMax	=	(b1>b2) ? b1 : b2 ;
		IntType		bMin	=	(b1>b2) ? b2 : b1 ;
		
		IntType		topOverBiggerBottom	=	factorial<IntType>(inN,bMax) ;
		IntType		smallerBottom		=	factorial<IntType>(bMin) ;
		
		return topOverBiggerBottom / smallerBottom ;
	}
	
	
	template <typename FloatType>
	static inline FloatType	power		( FloatType inBase , FloatType inExponent ) {
		if ( inExponent == 2 )
			return inBase * inBase ;
		return pow(inBase,inExponent) ;
	}
	
	template <typename FloatType>
	static inline FloatType	sqrt		( FloatType inValue ) {
		return ::sqrt(inValue) ;
	}
	
	template <typename SignedType> // can be float
	static inline SignedType	abs		( SignedType inValue ) {
		if ( inValue < 0 )
			return - inValue ;
		return inValue ;
	}
	
	
	template <typename FloatType>
	static inline FloatType	sin			( FloatType inValue ) {
		return ::sin(inValue) ;
	}
	template <typename FloatType>
	static inline FloatType	cos			( FloatType inValue ) {
		return ::cos(inValue) ;
	}
	
	template <typename FloatType>
	static inline FloatType	arcsin		( FloatType inValue ) {
		return ::asin(inValue) ;
	}
	template <typename FloatType>
	static inline FloatType	arccos		( FloatType inValue ) {
		return ::acos(inValue) ;
	}
	template <typename FloatType>
	static inline FloatType	arctan		( FloatType inValue ) {
		return ::atan(inValue) ;
	}
	
	template <typename FloatType>
	static inline FloatType	Arg			( FloatType inX , FloatType inY ) {
		if ( inX > 0 )				// R.H.P.
			{
			if ( inY == 0 )				// Straight right
				return 0 ;
			return IAMath :: arctan ( inY/inX ) ;
			}
		else if ( inX < 0 )			// L.H.P.
			{
			if ( inY == 0 )				// Straight left
				return halfPi() ;
			if ( inY > 0 )				// Quadrant 2
				return IAMath :: arctan ( inY/inX )  +  IAMath::halfPi() ;
			if ( inY < 0 )				// Quadrant 3
				return IAMath :: arctan ( inY/inX )  -  IAMath::halfPi() ;
			}
		return 0 ;
	}
	
	
	
	static inline double		pi () {
		return 3.14159265358979 ;
	}
	static inline double		halfPi () {
		return 1.5707963267949 ;
	}
	
	
	
	// Approximations
	template <typename FloatType>
	static FloatType	tanAcos			( FloatType inValue ) {
		if ( inValue < 0 )
			return - tanAcos ( -inValue ) ;
		
		// tanAcos(x) = sqrt(1-x^2) / x
		
		// For low values, approx. the numerator by 1-1/2*x^2
		if ( inValue < 0.625 ) // experimental number, from graph
			{
			FloatType	sq_2	=	inValue * inValue * 0.5 ;
			return ( 1. - sq_2 ) / inValue ;
			}
		
		// For higher values, we use taylor approx around 0.75
		// and use terms 0, 1 and 3
		static	FloatType	k0		=	0.5519171037 ;	// sqrt(7) / 3
		static	FloatType	k1		=	2.687747364 ;	// 64/63*sqrt(7)
		static	FloatType	k3		=	5.655849328 ;	// 59392*sqrt(7)/27783
		FloatType			shift1	=	inValue - 0.75 ;
		FloatType			shift3	=	shift1 * shift1 * shift1 ;
		
		return k0 - (k1*shift1) - (k3*shift3) ;
	} // valid [-1...1]
	
	
	
	// Angles
	
	template <typename FloatType>
	static inline FloatType	radiansToDegrees	( FloatType inValue ) {
		return inValue * 57.2957795130823 ;
	}
	template <typename FloatType>
	static inline FloatType	degreesToRadians	( FloatType inValue ) {
		return inValue * 0.01745329251994 ;
	}
	
	template <typename FloatType>
	static inline FloatType	standardizedDegrees	( FloatType inValue ) {
		while ( inValue > 180. )
			inValue -= 180. ;
		while ( inValue < -180. )
			inValue += 180. ;
		return inValue ;
	}
	template <typename FloatType>
	static inline FloatType	standardizedRadians	( FloatType inValue ) {
		while ( inValue > pi() )
			inValue -= pi() ;
		while ( inValue < -pi() )
			inValue += pi() ;
		return inValue ;
	}
} ;









#endif
